/*
 * Created on Mar 3, 2004
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package littlecyberwar.control;

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.text.Document;

import littlecyberwar.ui.*;
import littlecyberwar.model.*;
import littlecyberwar.util.*;
import littlecyberwar.util.Util;
import littlecyberwar.ui.overlay.*;

import ModuleBuilder.model.*;
import ModuleBuilder.UI.tree.*;



/**
 * @author Milton  Soong
 *
 * This is a delegate class that handles loading of a scenario==> the game
 * This class is a singleton
 */
public class ScenarioManager {
	private static MainFrame mainFrame;
	private static GameParameterModel gameParam;
	private static JLabel progText;
	private static ScenarioModel scenarioModel;
	
	//DEBUG
	private static HashMap ovMap;
	
	static {
		ovMap = new HashMap();
		ovMap.put(OverlayInstance._Type, OverlayInstance._Type_Edge);
		ovMap.put(OverlayInstance._Orientation, OverlayInstance._Orientation_Front);
		ovMap.put(OverlayInstance._Dimension_angle, new Double(0.0));
		ovMap.put(OverlayInstance._Dimension_distance, new Integer(96));
		ovMap.put(OverlayInstance._Color, Color.LIGHT_GRAY);
		ovMap.put(OverlayInstance._Alpha, new Float(0.4));
		ovMap.put(OverlayInstance._Type_Edge_Size, new Integer(96));
	}
	
	public static void setMainFrame( MainFrame in ){
		mainFrame = in;		
	}
	
	public static void setupGameState( ScenarioModel scenario, JLabel progTextIn ) {
		scenarioModel = scenario;
		progText = progTextIn;
		GameParameterModel paramMod = scenario.getGameParamModel();
		setupGameParameter(paramMod);
		progText.setText("Setting up game map...");
		MapModel map = scenario.getMapModel();
		setupMap(map);
		// next setup the Units.
		// There shouldn't be any reason to explicitly setup the UnitProfile since
		// they are linked into the UNit themselves.
		//
		progText.setText("Setting up Units...");
		DefaultMutableTreeNode root = scenario.getTreeRoot();
		setupScenarioDetail(root);
		setupUnits(root);
		setupProfile(root);
		setupRemoveToTrayDialog();
		setupChangeLayerDialog();
	}
	
	/**
	 * Get the scenario detail information (i.e. name of scenario, scale, distance unit)
	 * And later on the scenario descriptions.
	 * 
	 * @param root
	 */
	private static void setupScenarioDetail( DefaultMutableTreeNode root ){
		DefaultMutableTreeNode scnNode = ModuleBuilder.UI.tree.Util.findSpecificNodeType(root, GameTreeEditor.SCENARIO_DETAIL_EDITOR);
		if ( scnNode != null ) {
			ScenarioDetailModel gameMod = (ScenarioDetailModel)scnNode.getUserObject();
			String scenarioName = (String)gameMod.get(ScenarioDetailEditor._scenarioDetail_name);
			String distanceUnit = (String)gameMod.get(ScenarioDetailEditor._scenarioDetail_distanceUnit);
			if ( distanceUnit == null ){
				// setup default distance unit
				distanceUnit = ScenarioDetailEditor._defaultMapDistanceUnit;
			}
			
			Double mapScale = (Double)gameMod.get(ScenarioDetailEditor._scenarioDetail_scale);
			if ( mapScale == null ){
				mapScale = ScenarioDetailEditor._defaultMapScale;
			}
			
			Document doc = (Document)gameMod.get(ScenarioDetailEditor._scenarioDetail_notes);
			
			littlecyberwar.util.Util.setScenarioName(scenarioName);
			littlecyberwar.util.Util.setMapDistanceUnit(distanceUnit);
			littlecyberwar.util.Util.setMapScale(mapScale.doubleValue());
			littlecyberwar.util.Util.setScenarioNotes(doc);
					
		} else {
			Debug.out("No scenario detail node defined, older version of scenario, use default units");
		}
		return;
	}
	
	/**
	 * 1. find the Unit Node
	 * 2. iterate over the first level nodes (these are the Trays)
	 * 3. Fro ea. tray call subsquent nodes (these are the sub trays)
	 * 4. For ea. sub tray, add the unit into it.
	 * 
	 * @param root the root node of the tree 
	 */
	private static void setupUnits(DefaultMutableTreeNode root) {
		DefaultMutableTreeNode obNode = ModuleBuilder.UI.tree.Util.findOBNode(root);
		if ( obNode != null ) {
			Enumeration e = obNode.children();
			while ( e.hasMoreElements()){
				DefaultMutableTreeNode currNode = (DefaultMutableTreeNode)e.nextElement();
				setupTray(currNode);
			}
		} else {
			Debug.out("Why is there no obNode in the tree?");
		}
		return;
	}
	
	private static void setupTray( DefaultMutableTreeNode trayNode ) {
		UnitModel trayUnit = (UnitModel)trayNode.getUserObject();
		littlecyberwar.model.TrayModel trayMod = new littlecyberwar.model.TrayModel();
		String trayName = trayUnit.getName();
		progText.setText("Setting up tray: " + trayName + "...");
		// if there are stands associated with this unit, then this is a "commander" stand
		// create an element out of it and add it to the tray
		if ( trayUnit.hasStands() ) {
			Element CommanderEl=null;
			if ( !trayUnit.hasElement() ) {
				CommanderEl = setupUnitElement(trayUnit);
			}
			
			Vector v = trayMod.addNewTray("Leader");
			UnitTrayView trayVCmdr = new UnitTrayView( trayName, CommanderEl );
			v.addElement( trayVCmdr );
		}
		
		// now loop through each node, which is either a subTray or a unit
		Enumeration e = trayNode.children();
		while (e.hasMoreElements()){
			DefaultMutableTreeNode currNode = (DefaultMutableTreeNode)e.nextElement();
			GameModel gameMod = (GameModel)currNode.getUserObject();
			setupSubTray(currNode, trayMod);
		}
		
		// now add the tray  model to the main menu
		addTrayToMenu(trayName, trayMod);
		return;
	}
	
	private static void addTrayToMenu(String trayName, littlecyberwar.model.TrayModel trayMod) {
		MainFrame mainframe = (MainFrame)littlecyberwar.util.Util.lookup.get(littlecyberwar.util.Util._MAIN_FRAME);
		mainframe.addNewTray(trayName, trayMod);
	}
	
	/**
	 * this node and everything below it will go into a new sub tray.
	 * The name of the sub tray will be the name of the subTrayNode.
	 * The created subtray is then added to the TrayMod
	 * 
	 * @param currNode the root that is one level right below the leader node 
	 * @param trayMod the Tray Model that's to be used to add items
	 */
	private static void setupSubTray(DefaultMutableTreeNode subTrayNode, littlecyberwar.model.TrayModel trayMod){
		UnitModel topUnit = (UnitModel)subTrayNode.getUserObject();
		String subtrayName = topUnit.getName();
		Vector v = trayMod.addNewTray(subtrayName);

		// now let's do all the decendents
		// !!! Doesn't this include the subtray unit also?
		Enumeration e = subTrayNode.depthFirstEnumeration();
		while ( e.hasMoreElements() ){
			DefaultMutableTreeNode currNode = (DefaultMutableTreeNode)e.nextElement();
			UnitModel unitMod = (UnitModel)currNode.getUserObject();
			Element currEl = setupUnitElement(unitMod);
			if ( currEl != null ){
				UnitTrayView trayVCmdr = new UnitTrayView( unitMod.getName(), currEl );
				v.addElement( trayVCmdr );
			}
		}
		return; 		
	}
	/**
	 * Called when inputing a iUnit, and then we'll just create and link up a Unit with its element
	 *
	 */
	public static Element setupUnitElement(UnitModel unitMod) {
		Element el = null;
		// !!! Right now only assume that there are one image in the unit
		ArrayList stands = unitMod.getStands();
		if (stands.size() == 0){
			return ( el );
		}
		
		Stand firstStand = (Stand)stands.get(0);
		ImageIcon img = firstStand.getTopImage().getFullImageIcon();
		if ( img == null ){
			Debug.out("Why is imageIcon from model null?");
		}
		// call setImage explicitly so that the image gets loaded properly from memory
		Image iconImage = img.getImage();
		img.setImage(iconImage);
		
		ImageElement sideImgElement = unitMod.getSideImage();
		ImageIcon sideImg = null;
		if (sideImgElement != null) {
			sideImg = sideImgElement.getFullImageIcon();
			if ( sideImg != null ){
				iconImage = sideImg.getImage();
				sideImg.setImage(iconImage);
			}		
		}
		// next check if there already is an Element, if there is, then 
		// return that 
		if ( unitMod.hasElement() ){
			el = unitMod.getElement();
			//Debug.out("At before setting stuff setupUnitElement: trans=" + el.getTransform().toString());
			el.setupFrontCornerPoints();
			el.setupBaseRectangle();
			el.setupFinalShape();	// has to be after setupBaseRectangle
			//Debug.out("At setupUnitElement: trans=" + el.getTransform().toString());
			
		} else {
			// there wasn't an existing element, must setup
			el = new Element( 0, 0, img, sideImg);
		}
		linkupUnitAndElement(unitMod, el); 

		// !!! Overlay DEBUG STUFF
		// debugSetupOverlay(el);


		// if the element is already onBoard, then add it back to the board
		if ( el.isOnboard() ) {
			PlayingBoardModel model = (PlayingBoardModel)littlecyberwar.util.Util.lookup.get(littlecyberwar.util.Util._PLAYING_BOARD_MODEL);
			model.addElement( el );
			el = null;
		}	
		
		// setup distance moved value to 0 if it did not already exist
		Object o = unitMod.getHashValue(littlecyberwar.util.Util._elementAttribDistanceMoved);
		if ( o == null ) {
			unitMod.addHashValue(littlecyberwar.util.Util._elementAttribDistanceMoved, new Double(0.0));
		}	
		
		return ( el );
	}
	
	private static void linkupUnitAndElement( UnitModel unitMod, Element el ){
		el.setName (unitMod.getName());
		el.setUnit(unitMod);
		unitMod.setElement(el);
		Integer eid = unitMod.getId();		
		el.setElementId( eid );
		ElementIdDictionary.put( eid, el );	
		return;		
	}
	
	public static void setupGameParameter(GameParameterModel param) {
		gameParam = param;
	}
	
	/**
	 * Input a iMap object, setup the map
	 *
	 */
	public static void setupMap(MapModel map) {
		PlayingBoardPanel pbp = mainFrame.getPbp();
		ImageElement[][] allImages = map.getMapImageArray();
		int col = gameParam.getMapNumOfCol();
		int row = gameParam.getMapNumOfRow();
		pbp.setMapImage(allImages, col, row);
		MiniMapPanel mmp = mainFrame.getMmp();
		ImageElement el = allImages[0][0];
		ImageIcon largeImage = el.getFullImageIcon();
		Dimension largeMapD = pbp.getMapSize();
		mmp.setMapImage(allImages, col, row, largeMapD);
		JScrollPane scrollMap = mainFrame.getScrollMap();
		scrollMap.revalidate();
		JViewport vp = scrollMap.getViewport();
		mmp.setViewPort(vp);
		ControlPanel cp = mainFrame.getControlP();
		cp.validate();
		mainFrame.validate();
	}
	
	/**
	 * @return
	 */
	public static ScenarioModel getScenarioModel() {
		return scenarioModel;
	}
	
	/**
	 * This is called to initialize the profiles. For each profile one is created a ProfilePanel
	 * And they are indexed by the profile name into a HashMap
	 * 
	 * @param root
	 */
	private static void setupProfile(DefaultMutableTreeNode root) { 
		DefaultMutableTreeNode proNode = ModuleBuilder.UI.tree.Util.findProfileNode(root);
		if ( proNode != null ) {
			Enumeration e = proNode.children();
			while ( e.hasMoreElements()){
				DefaultMutableTreeNode currNode = (DefaultMutableTreeNode)e.nextElement();
				setupIndividualProfilePanel(currNode);
			}
		} else {
			Debug.out("Why is there no obNode in the tree?");
		}
		return;
		
	}
	
	/**
	 * Will get all the attributes from a given unit profile
	 * run through them, and create a panel That contains each pair of attributes
	 * Then add the attributes into the lookup table, and
	 * Also add it to the ProfileCard panel
	 * 
	 * @param node contains the treeNode that contains userObject= UnitProfileModel
	 */
	private static void setupIndividualProfilePanel(DefaultMutableTreeNode node){
		UnitProfileModel proMod = (UnitProfileModel)node.getUserObject();
		ProfilePanel pp = new ProfilePanel(proMod);
		littlecyberwar.util.Util.profilePanelLookup.put(proMod, pp);
		ProfileCardPanel profPanel = mainFrame.getProfilePanel();
		profPanel.add(pp, proMod.getName());		
	}
	
	/**
	 * At this point all the trays and subtrays should be setup already.
	 * Now we need to build the remove to tray dialog Here
	 *
	 */
	private static void setupRemoveToTrayDialog() { 
		MainFrame mainframe = (MainFrame)littlecyberwar.util.Util.lookup.get(littlecyberwar.util.Util._MAIN_FRAME);
		HashMap allTrays = mainframe.getAllTrays();
		RemoveToTrayDialog removeDiag = RemoveToTrayDialog.getInstance();
		
		// iterate through all the outer trays
		Iterator it = allTrays.entrySet().iterator();
		while ( it.hasNext() ){
			Map.Entry e = (Map.Entry)it.next();
			String TrayName = (String)e.getKey();
			// Debug.out("---TrayName= " + TrayName);
			littlecyberwar.model.TrayModel trayMod = (littlecyberwar.model.TrayModel)e.getValue();
			
			// now iterate through all the subtray names
			Set allSubTrays = trayMod.getAllTrayNames();
			Iterator subIt = allSubTrays.iterator();
			ArrayList allSubTrayNames = new ArrayList();			
			while ( subIt.hasNext() ){
				Object o = subIt.next();
				String subTrayName = (String)o;
				// Debug.out("----- SubTrayName= " + subTrayName); 
				allSubTrayNames.add(subTrayName);
			}
			removeDiag.addEntry(TrayName, allSubTrayNames);
		}
		InternalCmdFacade mod = mainframe.getIcmd();
		removeDiag.compose(mod);
		RemoveAction action = RemoveAction.getInstance();
		action.setRemoveDialog(removeDiag);
		return;
	}

/*
 * In the future the name of the layers could be passed in from the scenario
 */
	private static void setupChangeLayerDialog() { 
		MainFrame mainframe = (MainFrame)littlecyberwar.util.Util.lookup.get(littlecyberwar.util.Util._MAIN_FRAME);
		ChangeLayerDialog layerDiag = ChangeLayerDialog.getInstance();
		
		// now loop through the name of all layers and call addEntry
		// index is off by one because of zero origin
		//
		layerDiag.addEntry("5- Top", "4");
		layerDiag.addEntry("4", "3");
		layerDiag.addEntry("3- Default", "2");
		layerDiag.addEntry("2", "1");
		layerDiag.addEntry("1- Bottom", "0");
		
		InternalCmdFacade mod = mainframe.getIcmd();
		layerDiag.compose(mod);
		ChangeLayerAction action = ChangeLayerAction.getInstance();
		action.setChangeLayerDialog(layerDiag);
		return;
	}

	/**
	 * @return
	 */
	public static JLabel getProgText() {
		return progText;
	}

	/**
	 * @param label
	 */
	public static void setProgText(JLabel label) {
		progText = label;
	}

	private static void processOverlayInstance(String overlayId, Element currEl){
		
		String modelKey = Util.getOverlayModelLookupKey(overlayId);
		HashMap hm = (HashMap)currEl.getNewProps(modelKey);
		OverlayInstance ovi = OverlayInstance.getInstance(hm);
		String InstanceKey = Util.getElementHashLookupOverlayInstance(overlayId);
		currEl.setNewProps(InstanceKey, ovi);
		return;
	}
	
	private static void debugSetupOverlay(Element currEl) {
		currEl.setNewProps(Util.getOverlayModelLookupKey("TEST"), ovMap);
		processOverlayInstance("TEST", currEl);
	}
	

}
